#include <timer.h>
#include <device.h>
#include <errno.h>

static int timer_open(struct device *dev)
{
	struct timer *tm = device_to_timer(dev);
	struct timer_driver *tm_drv = device_to_timer_driver(dev);
	int ret = 0;
	
	dev_flags_clr_can_seek(dev->flags);
	dev_flags_clr_can_read(dev->flags);
	dev_flags_clr_can_write(dev->flags);
	dev_flags_clr_can_nonblock(dev->flags);
	dev_flags_clr_can_sync(dev->flags);
	
	dev_oflags_clr_read(dev->oflags);
	dev_oflags_clr_write(dev->oflags);
	dev_oflags_clr_seek(dev->oflags);
	dev_oflags_clr_nonblock(dev->oflags);
	dev_oflags_clr_sync(dev->oflags);
	
	if (tm_drv->ops->startup)
		ret = tm_drv->ops->startup(tm);	
		
	return ret;
}

static void timer_close(struct device *dev)
{
	struct timer *tm = device_to_timer(dev);
	struct timer_driver *tm_drv = device_to_timer_driver(dev);

	if (tm_drv->ops->shutdown)
		tm_drv->ops->shutdown(tm);
}

static int no_read(struct device *dev, void *buf, unsigned int len)
{
	return -ENOTSUPP;
}

static int no_write(struct device *dev, const void *buf, unsigned int len)
{
	return -ENOTSUPP;
}

static loff_t no_llseek(struct device *dev, loff_t offset, unsigned int origin)
{
	return -ENOTSUPP;
}

static void no_flush(struct device *dev)
{
}

static int timer_match(struct device *dev, struct driver *drv)
{
	struct timer_driver *tm_drv = driver_to_timer_driver(drv);

	if (tm_drv->ops->match)
		return tm_drv->ops->match(dev, tm_drv);
	
	return -ENOTSUPP;
}

static int timer_probe(struct device *dev, struct driver *drv)
{
	struct timer *tm = device_to_timer(dev);
	struct timer_driver *tm_drv = driver_to_timer_driver(drv);

	if (tm_drv->ops->probe)
		return tm_drv->ops->probe(tm, tm_drv);
	
	return 0;
}

static int timer_ioctl(struct device *dev, unsigned int cmd, unsigned int arg)
{
	struct timer *tm = device_to_timer(dev);
	struct timer_driver *tm_drv = device_to_timer_driver(dev);
	void *argp = (void *)arg;
	int ret = 0;
	int64_t ret64 = 0;

	switch(cmd) {
		case TIMER_IO_START:
			ret = tm_drv->ops->start(tm);
			break;
		case TIMER_IO_STOP:
			ret = tm_drv->ops->stop(tm);
			break;
		case TIMER_IO_SET_INTERVAL:
			ret = tm_drv->ops->set_interval(tm, *((uint64_t *)argp));
			break;
		case TIMER_IO_GET_INTERVAL:
			ret64 = tm_drv->ops->get_interval(tm);
			if (!IS_ERR(ret64)) {
				*((uint64_t *)argp) = (uint64_t)ret64;
				ret = 0;
			}			
			break;
		case TIMER_IO_SET_TYPE:
			ret = tm_drv->ops->set_type(tm, arg);
			break;
		case TIMER_IO_GET_TYPE:
			ret = tm_drv->ops->get_type(tm);
			if (!IS_ERR(ret)) {
				*((uint32_t *)argp) = (uint32_t) ret;
				ret = 0;
			}			
			break;
		case TIMER_IO_SET_CALLBACK:
			ret = tm_drv->ops->set_callback(tm, argp);
			break;
		case TIMER_IO_GET_TIME:
			ret64 = tm_drv->ops->get_time(tm);
			if (!IS_ERR(ret64)) {
				*((uint64_t *)argp) = (uint64_t)ret64;
				ret = 0;
			}			
			break;
		default:
			if (tm_drv->ops->ioctl) 
				return tm_drv->ops->ioctl(tm, cmd, arg);
			break;
	}
	
	return ret;	
}

struct device_operations timer_ops = {
	.open	= timer_open,
	.close	= timer_close,
	.read	= no_read,
	.write	= no_write,
	.seek	= no_llseek,
	.ioctl	= timer_ioctl,
	.flush	= no_flush,
	.match  = timer_match,
	.probe  = timer_probe,
};

